🎖️GitЯра🎖️
Commit ea48859781afbe77d74a32b14459e227bf566c42
Parents : 1089e43
Author : James Rich <james.a.rich@gmail.com>
Date : 2026-05-18T21:37:59-05:00
feat: add environment metrics row to compact view
Shows temp, humidity, and barometric pressure as icon + value only
(no labels). Controlled by the 'Environment Metrics' toggle.
Uses FlowRow for wrapping on narrow screens.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Changes
6 files changed, 78 insertions(+), 1 deletions(-)
Diff
diff --git a/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/NodeItemCompact.kt b/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/NodeItemCompact.kt
index 0105a1c897..90bc66d945 100644
--- a/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/NodeItemCompact.kt
+++ b/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/NodeItemCompact.kt
@@ -48,6 +48,7 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import org.jetbrains.compose.resources.stringResource
import org.jetbrains.compose.resources.vectorResource
+import org.meshtastic.core.common.util.MetricFormatter
import org.meshtastic.core.model.Node
import org.meshtastic.core.model.isUnmessageableRole
import org.meshtastic.core.model.util.toDistanceString
@@ -59,9 +60,12 @@ import org.meshtastic.core.resources.unknown_username
import org.meshtastic.core.ui.icon.DeviceSleep
import org.meshtastic.core.ui.icon.Distance
import org.meshtastic.core.ui.icon.Favorite
+import org.meshtastic.core.ui.icon.Humidity
import org.meshtastic.core.ui.icon.MeshtasticIcons
import org.meshtastic.core.ui.icon.MqttConnected
+import org.meshtastic.core.ui.icon.Pressure
import org.meshtastic.core.ui.icon.Success
+import org.meshtastic.core.ui.icon.Temperature
import org.meshtastic.core.ui.icon.Unmessageable
import org.meshtastic.core.ui.theme.StatusColors.StatusYellow
import org.meshtastic.proto.Config
@@ -89,6 +93,7 @@ fun NodeItemCompact(
showChannel: Boolean = true,
showRole: Boolean = true,
showTelemetry: Boolean = true,
+ tempInFahrenheit: Boolean = false,
) {
val longName = thatNode.user.long_name.ifEmpty { stringResource(Res.string.unknown_username) }
val isFavorite = thatNode.isFavorite
@@ -193,7 +198,16 @@ fun NodeItemCompact(
contentColor = contentColor,
)
- // Row 3: Tertiary metadata — hardware · role · hops · channel · telemetry
+ // Row 3: Environment metrics — temp · humidity · pressure (icon + value only)
+ if (showTelemetry) {
+ CompactMetricsRow(
+ thatNode = thatNode,
+ tempInFahrenheit = tempInFahrenheit,
+ contentColor = contentColor,
+ )
+ }
+
+ // Row 4: Tertiary metadata — hardware · role · hops · channel
CompactFooterRow(
thatNode = thatNode,
isThisNode = isThisNode,
@@ -424,6 +438,68 @@ private fun CompactFooterRow(
}
}
+@Composable
+@Suppress("CyclomaticComplexMethod")
+private fun CompactMetricsRow(thatNode: Node, tempInFahrenheit: Boolean, contentColor: Color) {
+ val env = thatNode.environmentMetrics
+ val segments =
+ buildList<@Composable () -> Unit> {
+ if ((env.temperature ?: 0f) != 0f) {
+ val temp = MetricFormatter.temperature(env.temperature ?: 0f, tempInFahrenheit)
+ add {
+ IconInfo(
+ icon = MeshtasticIcons.Temperature,
+ contentDescription = "Temperature",
+ contentColor = contentColor,
+ text = temp,
+ )
+ }
+ }
+ if ((env.relative_humidity ?: 0f) != 0f) {
+ add {
+ IconInfo(
+ icon = MeshtasticIcons.Humidity,
+ contentDescription = "Humidity",
+ contentColor = contentColor,
+ text = MetricFormatter.humidity(env.relative_humidity ?: 0f),
+ )
+ }
+ }
+ if ((env.barometric_pressure ?: 0f) != 0f) {
+ add {
+ IconInfo(
+ icon = MeshtasticIcons.Pressure,
+ contentDescription = "Pressure",
+ contentColor = contentColor,
+ text = MetricFormatter.pressure(env.barometric_pressure ?: 0f),
+ )
+ }
+ }
+ }
+
+ if (segments.isNotEmpty()) {
+ @OptIn(ExperimentalLayoutApi::class)
+ FlowRow(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.spacedBy(4.dp),
+ verticalArrangement = Arrangement.spacedBy(2.dp),
+ ) {
+ segments.forEachIndexed { index, content ->
+ Row(verticalAlignment = Alignment.CenterVertically) {
+ if (index > 0) {
+ Text(
+ text = "· ",
+ style = MaterialTheme.typography.labelMedium,
+ color = contentColor.copy(alpha = 0.5f),
+ )
+ }
+ content()
+ }
+ }
+ }
+ }
+}
+
private fun isFutureDate(lastHeard: Int): Boolean {
val nowSeconds = org.meshtastic.core.common.util.nowSeconds.toInt()
val oneYearSeconds = 365 * 24 * 60 * 60
diff --git a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/list/NodeListScreen.kt b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/list/NodeListScreen.kt
index 238c81bc9d..c800c22a5d 100644
--- a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/list/NodeListScreen.kt
+++ b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/list/NodeListScreen.kt
@@ -261,6 +261,7 @@ fun NodeListScreen(
showChannel = showChannel,
showRole = showRole,
showTelemetry = showTelemetry,
+ tempInFahrenheit = state.tempInFahrenheit,
)
}
val isThisNode = remember(node) { ourNode?.num == node.num }
diff --git a/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/SettingsScreenshotTestsKt/ScreenshotNodeLayoutSettingsCompact_Dark_d19fbf1f_0.png b/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/SettingsScreenshotTestsKt/ScreenshotNodeLayoutSettingsCompact_Dark_d19fbf1f_0.png
index 5ddf15e649..c085a92ff4 100644
Binary files a/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/SettingsScreenshotTestsKt/ScreenshotNodeLayoutSettingsCompact_Dark_d19fbf1f_0.png and b/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/SettingsScreenshotTestsKt/ScreenshotNodeLayoutSettingsCompact_Dark_d19fbf1f_0.png differ
diff --git a/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/SettingsScreenshotTestsKt/ScreenshotNodeLayoutSettingsCompact_Light_b29dc7a7_0.png b/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/SettingsScreenshotTestsKt/ScreenshotNodeLayoutSettingsCompact_Light_b29dc7a7_0.png
index 3f5296ee1f..00c36d89ea 100644
Binary files a/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/SettingsScreenshotTestsKt/ScreenshotNodeLayoutSettingsCompact_Light_b29dc7a7_0.png and b/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/SettingsScreenshotTestsKt/ScreenshotNodeLayoutSettingsCompact_Light_b29dc7a7_0.png differ
diff --git a/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/SettingsScreenshotTestsKt/ScreenshotSampleNodeCompactAllFields_Dark_d19fbf1f_0.png b/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/SettingsScreenshotTestsKt/ScreenshotSampleNodeCompactAllFields_Dark_d19fbf1f_0.png
index be38169585..608200e361 100644
Binary files a/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/SettingsScreenshotTestsKt/ScreenshotSampleNodeCompactAllFields_Dark_d19fbf1f_0.png and b/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/SettingsScreenshotTestsKt/ScreenshotSampleNodeCompactAllFields_Dark_d19fbf1f_0.png differ
diff --git a/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/SettingsScreenshotTestsKt/ScreenshotSampleNodeCompactAllFields_Light_b29dc7a7_0.png b/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/SettingsScreenshotTestsKt/ScreenshotSampleNodeCompactAllFields_Light_b29dc7a7_0.png
index 90b0e1cd18..5687818b51 100644
Binary files a/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/SettingsScreenshotTestsKt/ScreenshotSampleNodeCompactAllFields_Light_b29dc7a7_0.png and b/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/SettingsScreenshotTestsKt/ScreenshotSampleNodeCompactAllFields_Light_b29dc7a7_0.png differ
Served by rngit 1.5.0 - Generated in 0.11s